package FileSystem;

import java.util.*;
import java.io.*;
import java.nio.channels.FileChannel;

import Mask.MaskFilter;

/******************************************** Интерфейсы **********************************************/

interface Copy
{
	boolean copy (String src, String dest); 
}

interface Rename
{
	boolean rename (String src, String dest); 
}

interface Delete
{
	boolean delete (String src); 
}

interface Create
{
	boolean createFile (String src, String name); 
	boolean createDirectory (String src, String name); 
}

interface Find
{
	Collection find (String src, String mask); 
}

interface Sort
{
	Collection sortByName (File dir, int type);/* 0 - ASC  ïî âîçðàñòàíèþ è 1 - DESC  ïî óáûâàíèþ */
	Collection sortBySize (File dir, int type);
	Collection sortByLastModify (File dir, int type);
}

/********************************* Классы реализации интерфейсов *********************************************/

public class FileWorker implements Copy, Rename, Delete, Create, Find, Sort, Comparator {

	public File hendDir; // Currend directory // Текущая директория
	
	public String textError; // Text of an error // Текст возможной ошибки
	
	
/* *********** Constructors ********** */
	public FileWorker (String startDir) /*overload*/ {
		hendDir = new File(startDir);
	}
	
	public FileWorker (File startDir) /*overload*/ {
		hendDir = startDir;
	}
	
/* ********* Public methods ********** */

	/* Change directory */
	public void cd (String newDir) /*overload*/ {
		String _ = new String(hendDir.toString() + File.separator + newDir);
		
		cd(new File(_));
	}
	
	public void cd (File newDir) /*overload*/ {
		try {					
			if (newDir.getAbsoluteFile().isDirectory()) {
				if (!newDir.canRead()) {
					System.err.println(newDir.getName()+": no access to directory.");
				}
				
				hendDir = newDir.getCanonicalFile();
			} else {
				System.err.println(newDir.getName() + ": no such directory."); // Перейти в файл - глупая идея
			}
				
		} catch (IOException e) {
			System.err.println(e);
		}
	}
	
	/* Copy file or directory */
	public boolean copy (String src, String dest) {
		return copy (new File(src), new File(dest));
	}
	
	public boolean copy (File src, File dest) {
		try {
			File _src;
			File _dest;
			
			if (src.isAbsolute()) {
				_src = src;
			} else {
				_src = new File (hendDir + File.separator + src);
			}
	
			if (!_src.exists()) { // Source not exists // Источника не существует
				this.textError = src.toString() + ": not exists.";
				return false;
			}
				
			if (!_src.canRead()) {
				this.textError = src.toString() + ": no access to read.";
				return false;
			}
			
			
			if (dest.isAbsolute()) {
				_dest = dest;
			} else {
				_dest = new File (hendDir + File.separator + dest);
			}
			
			// debug-information
			//System.err.println("src="+src +" dest="+dest);
			
			
			FileWorker destDir = new FileWorker (_dest); // Объект, обозначающий папку-назначение
			
			if (_src.isFile()) { // Sourse is file // Источник - файл
				if (destDir.createFile(_src.getName())) {
					//InputStream in = new FileInputStream(_src);
					//OutputStream out = new FileOutputStream(_dest);
					
					// debug-information
					//System.err.println("file-dest="+_dest + File.separator + _src.getName());
					
					FileInputStream in = new FileInputStream(_src);
					FileOutputStream out = new FileOutputStream(_dest + File.separator + _src.getName());
					
					FileChannel srcChannel = in.getChannel();
					FileChannel destChannel = out.getChannel();
	
					srcChannel.transferTo(0, srcChannel.size(), destChannel);
					
					//byte[] buf = new byte[1024]; 	// buffer
					//int len;
					
					//while ((len = in.read(buf)) != 0)
					//	out.write(buf, 0, len);
					
					in.close();
					out.close();
					return true;
				} else {
					this.textError = _src.getName() + " not created1.";
					return false;
				}
			} else {
				if (destDir.createDirectory(_src.getName())) {
					Collection content = this.find(_src);
					
					for (Iterator i = content.iterator(); i.hasNext(); ) {
						File hend = (File) i.next(); // Current file to directory // Текущий файл в папке
						// debug-information
						//System.err.println("hend="+hend.getAbsolutePath());
						
						if (hend.isFile()) {
							destDir.copy(hend.toString(), destDir.hendDir + File.separator + _src.getName());
						} else {
							FileWorker newDir = new FileWorker(hend);
							if(!newDir.copy(hend.toString(), destDir.hendDir + File.separator + _src.getName()))
								System.err.println("Чё за нах?");
						}
            				}
            				return true;
				} else {
					this.textError = _src.getName() + " not created2.";
					return false;
				}
			}
		} catch (FileNotFoundException e) {
			this.textError = e.getMessage();
			return false;
		} catch (IOException e) {
			this.textError = e.getMessage();
			return false;
		}
	}
	
	/* Rename file or directory */
	public boolean rename (String src, String dest) /*overload*/ {	
		return rename (new File(src), new File(dest));
	}
	
	public boolean rename (File src, File dest) /*overload*/ {
		try {
			File _src;
			File _dest;
			
			if (src.isAbsolute()) {
				_src = src;
			} else {
				_src = new File(hendDir + File.separator + src); // Absolute path to file-source
			}
				
			if (!_src.exists()) {
				throw new NullPointerException("File not found");
			}
				
			if (dest.isAbsolute()) {
				_dest = dest;
			} else {
				_dest = new File(hendDir + File.separator + dest); // Absolute path to file-source
			}
//			_dest = new File(hendDir + File.separator + dest); // Absolute path to file-destination
			
//			if (_dest.exists()) {
//				_dest.delete();	// If a file exist, need to remove it // Если файл существует, его нужно удалить
//			}
			return _src.renameTo(_dest);	// Rename file // Переименование файла
		} catch(SecurityException e) {
			System.err.println(src + ": can not rename.");
			return false;
		} catch(NullPointerException e) {
			System.err.println(e);
			return false;
		}
	}
	
	/* Remove file or directory (recursive) */
	public boolean delete (File src) /*overload*/ {
		File _src;
		
		if (src.isAbsolute()) {
			_src = src;
		} else {
			_src = new File(hendDir + File.separator + src);
		}
		
		try {
			if (_src.isFile()) {
				return _src.delete();	// Delete file // Удаление файла
			} else {
				if (!_src.canWrite()) { // No access to write a directory
					return false;
				}
				
				Collection content = this.find(_src);  // If this folder, return its contents  // Если это папка, возвращаем её содержимое
			
				for (Iterator i = content.iterator(); i.hasNext(); ) {
					File hend = (File) i.next(); // Current file to directory // Текущий файл в папке
				
					if (hend.isFile())
						hend.delete(); // Файлы в папке удаляем итерационно
					else {
						FileWorker nextDir = new FileWorker(hend); // Create new object // Создаём новый объект
						nextDir.delete(nextDir.hendDir);	// and delete folder after it // и удаляем папку через него
					}
				} // После этого, по-хорошему, папка должна остаться пустой
			
				return _src.delete(); // ... и мы её грохаем
			}
		} catch (NullPointerException e) {
			System.err.println("Directory not found: " + this.hendDir);
			return false;
		}
	}
	
	public boolean delete (String src) /*overload*/ {
		return delete (new File(src));
	}
	
	/* Create a new file */
	public boolean createFile (String name) /*overload*/ {
		return createFile (new File("."), name);
	}
	
	public boolean createFile (File name) /*overload*/ {
		return createFile (new File("."), name.toString());
	}
	
	public boolean createFile (String src, String name) /*overload*/ {
		return createFile (new File(src), name);	
	}
	
	public boolean createFile (File src, String name) /*overload*/ {
		String pathToNewFile;
		
		if (src.isAbsolute()) {
			pathToNewFile = src + File.separator + name; // Абсолютный путь к файлу // Absolute path to file
		} else {
			pathToNewFile = this.hendDir + File.separator + src + File.separator + name; // Относительный путь преобразовываем к абсолютному
		}
		
		try {
			return (new File(pathToNewFile)).createNewFile(); // Create a new file (if possible)
		} catch (SecurityException e) {
			System.err.println(e);
			return false;
		} catch (IOException e) {
			System.err.println(e);
			return false;
		}
	}
	
	/* Create a new directory */
	public boolean createDirectory (String name) /*overload*/ {
		return createDirectory(new File("."), name);
	}
	
	public boolean createDirectory (File name) /*overload*/ {
		return createDirectory(new File("."), name.toString());
	}
	
	public boolean createDirectory (String src, String name) /*overload*/ {
		return createDirectory (new File(src), name);
	}
	
	public boolean createDirectory (File src, String name) /*overload*/ {
		String pathToNewDir;
		
		if (src.isAbsolute()) {
			pathToNewDir = src + File.separator + name;
		} else {
			pathToNewDir = this.hendDir + File.separator + src + File.separator + name; // Relative path convert to absloute path
		}
		
		// debug
//		System.err.println(pathToNewDir);
		
		try {
			return (new File(pathToNewDir)).mkdirs(); // Create a new file (if possible)
		} catch (SecurityException e) {
			System.err.println(e);
			return false;
		}
	}
	
	/* find files to input directory */
	public Collection find (String src) /*overload*/ {
		return find (new File(src), null);
	}
	
	public Collection find (File src) /*overload*/ {
		return find (src, null);
	}
	
	public Collection find (String src, String mask) /*overload*/ {
		return find(new File(src), mask);
	}
	
	public Collection find (File src, String mask) /*overload*/ {
		if (!src.exists())
			return null;
	
		String path;
		
		if (src.isAbsolute()) {	// Для корректной работы имя файла должно быть абсолютным
			path = src.toString();
		} else {
			path = hendDir.toString() + File.separator + src; // Относительное имя преобразуем в абсолютное
		}
		File _src = new File(path);
		
		if (!_src.isDirectory()) // src не является директорией // src is not a directory
			return null;
		
		MaskFilter pmask;
		if (mask == null)
			pmask = null;
		else {
			pmask = new MaskFilter();
			pmask.regExp(mask);
		}
		
		File[] files = _src.listFiles(pmask); // Список файлов // List files in directory
		
		ArrayList listFiles = new ArrayList();
		
		for (File f: files) {
			listFiles.add(f); // сохраняем список файлов // Save to list files
		}
		
		return listFiles; // возврат списка файлов // Return to list files
	}
	
	private byte typeSort;
	/* Типы сортировки:
	1 - по имени;
	2 - по размеру;
	3 - по дате последнего изменения.
	*/
	
	private boolean toInc; // true - по возврастанию, false - по убыванию
	
	public int compare (Object o1, Object o2) {
	/** Метод compare предназначен для сравнения объектов
	в методе sort() класса Collections.
	*/
		if (o1 != null && o2 != null &&
			o1 instanceof File && o2 instanceof File) {
	
			File f1 = (File) o1;
			File f2 = (File) o2;
			File _f1;
			File _f2;
			
			String path1 = hendDir + File.separator + f1.getName();
			String path2 = hendDir + File.separator + f2.getName();
			
			if (f1.isAbsolute())
				_f1 = f1;
			else 
				_f1 = new File(path1);
				
			if (f2.isAbsolute())
				_f2 = f2;
			else 
				_f2 = new File(path2);
			
			switch (typeSort) {
			case 1: // Sort by name
				
				if (toInc)
					return _f1.getName().compareTo(_f2.getName());
				else
					return _f2.getName().compareTo(_f1.getName());
			case 2: // Sort by size
				
				Long size1 = new Long(_f1.length());
				Long size2 = new Long(_f2.length());
				
				if (toInc)
					return size1.compareTo(size2);
				else
					return size2.compareTo(size1);
			case 3: // Sort by last modified
			
				Long date1 = new Long(_f1.lastModified());
				Long date2 = new Long(_f2.lastModified());
				
				if (toInc)
					return date1.compareTo(date2);
				else
					return date2.compareTo(date1);
			}
		}
		return 0;
	}
	
	/* Sort files in directory */
	public Collection sortByName (String dir, int type) {
		return sortByName (new File(dir), type);
	}
	
	public Collection sortByName (File dir, int type) {
		this.typeSort = 1;
		this.toInc = (type == 1);
		
		List files = (List) find(dir);
		Collections.sort(files, this);
		
		return files;
	}
	
	public Collection sortBySize (String dir, int type) {
		return sortBySize (new File(dir), type);
	}
	
	public Collection sortBySize (File dir, int type) {
		this.typeSort = 2;
		this.toInc = (type == 1);
	
		List files = (List) find(dir);
		Collections.sort(files, this);
		
		return files;
	}
	
	public Collection sortByLastModify (String dir, int type) {
		return sortByLastModify (new File(dir), type);
	}
	
	public Collection sortByLastModify (File dir, int type){
		this.typeSort = 3;
		this.toInc = (type == 1);
	
		List files = (List) find(dir);
		Collections.sort(files, this);
		
		return files;
	}
}
